package com.pks.datacenter.controller.api;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.pks.datacenter.aspect.AspectApiLogTag;
import com.pks.datacenter.common.WebContext;
import com.pks.datacenter.config.RedisKeyConfig;
import com.pks.datacenter.dto.ApiTokenTime;
import com.pks.datacenter.entity.api.ApiField;
import com.pks.datacenter.entity.api.ApiParams;
import com.pks.datacenter.entity.api.ApiPerm;
import com.pks.datacenter.entity.api.ApiToken;
import com.pks.datacenter.entity.api.ApiUser;
import com.pks.datacenter.entity.api.ApiMethod;
import com.pks.datacenter.entity.basic.DbField;
import com.pks.datacenter.entity.basic.DbInfo;
import com.pks.datacenter.entity.basic.DbSource;
import com.pks.datacenter.handler.RedisHandler;
import com.pks.datacenter.service.api.ApiFieldService;
import com.pks.datacenter.service.api.ApiLogService;
import com.pks.datacenter.service.api.ApiMethodService;
import com.pks.datacenter.service.api.ApiParamsService;
import com.pks.datacenter.service.api.ApiPermService;
import com.pks.datacenter.service.api.ApiRecordService;
import com.pks.datacenter.service.api.ApiTokenService;
import com.pks.datacenter.service.api.ApiUserService;
import com.pks.datacenter.service.basic.DbFieldService;
import com.pks.datacenter.service.basic.DbInfoService;
import com.pks.datacenter.service.basic.DbSourceService;
import com.pks.datacenter.utils.SUtilAes;
import com.pks.datacenter.utils.SUtilDb;
import com.pks.resultful.common.SRemoteResult;
import com.pks.util.SUtilDate;
import com.pks.util.SUtilRandom;
import com.pks.util.UtilConvert;
import com.pks.web.springmvc.controller.BasicController;

@RestController
@RequestMapping("/api")
@Scope("prototype")
@Component("apiAction")
public class ApiAction extends BasicController {

	private Logger logger = LoggerFactory.getLogger(ApiAction.class);

	@Autowired
	private RedisHandler redisHandler;
	@Autowired
	private ApiUserService apiUserService;
	@Autowired
	private ApiRecordService apiRecordService;
	@Autowired
	private ApiTokenService apiTokenService;
	@Autowired
	private ApiMethodService apiMethodService;
	@Autowired
	private ApiFieldService apiFieldService;
	@Autowired
	private DbInfoService dbInfoService;
	@Autowired
	private ApiParamsService apiParamsService;
	@Autowired
	private ApiPermService apiPermService;
	@Autowired
	private ApiLogService apiLogService;
	@Autowired
	private DbFieldService dbFieldService;
	@Autowired
	private DbSourceService dbSourceService;

	/**
	 * 
	 * @param type
	 *            1、 每日调用次数判断 2、调用间隔判断
	 * @param methodName
	 *            调用接口方法
	 * @param apiUserAccount
	 *            调用账号
	 * @param count
	 *            正常界限次数
	 * @param times
	 *            缓存时长（秒）
	 * @return
	 */
	public boolean invokeExceededTimes(Integer type, String methodName, String apiUserAccount,
			Long count, Long times) {
		if (type == null || StringUtils.isBlank(methodName) || StringUtils.isBlank(apiUserAccount)
				|| count == null || times == null) {
			return false;
		}
		if (type == 1) {
			String redisKey = "";
			redisKey = RedisKeyConfig.KEY_API_RECORD;
			String time_sdf = SUtilDate.SDF_DATE_NOSEP.get().format(new Date());
			String key = redisKey + time_sdf + ":" + apiUserAccount + "_" + methodName;
			if (redisHandler.hasKey(key)) {
				Integer redis_count = (Integer) redisHandler.get(key);
				if (redis_count >= count) {
					return false;
				}
				redisHandler.incr(key, 1L);
			} else {
				redisHandler.set(key, 1L, times);
			}
		} else if (type == 2) {
			String redisKey = "";
			redisKey = RedisKeyConfig.KEY_API_LASTUSETIME;
			String key = redisKey + apiUserAccount + "_" + methodName;
			long nowTime = new Date().getTime();
			if (redisHandler.hasKey(key)) {
				Long lastTime = (Long) redisHandler.get(key);
				if (nowTime - lastTime < times * 1000) {
					return false;
				}
			}
			redisHandler.set(key, nowTime, times);
		}
		return true;
	}

	@RequestMapping("/getApiToken")
	@AspectApiLogTag(isGetToken = true)
	public SRemoteResult<ApiToken> getApiToken(HttpServletRequest request,
			@RequestParam(required = true) String account,
			@RequestParam(required = true) String password) {
		// String methodParams = getParamsJsonByRequest(request);
		String ip = WebContext.getRealIpAddress(request);
		try {
			// 判断 10分钟内同一个ip 请求次数是否异常
			ApiTokenTime tokenTime = redisHandler.getApiTokenTime(ip);
			if (tokenTime != null) {
				if (SUtilDate.getDateByMinute(new Date(), -10).before(tokenTime.getTime())) {
					if (tokenTime.getCount() >= 5) {
						logger.warn("ip：" + ip + "，account:" + account + "，当前IP短时间内请求过于频繁，请10分钟后再试");
						return SRemoteResult.initError("当前IP短时间内请求过于频繁，请10分钟后再试");
					} else {
						tokenTime.setCount(tokenTime.getCount() + 1);
					}
				} else {
					tokenTime.setCount(1);
					tokenTime.setTime(new Date());
				}
			} else {
				tokenTime = new ApiTokenTime();
				tokenTime.setIp(ip);
				tokenTime.setCount(1);
				tokenTime.setTime(new Date());
			}
			redisHandler.setApiTokenTime(ip, tokenTime);
		} catch (Exception e) {
			e.printStackTrace();
		}

		String methodName = request.getRequestURL().toString();
		methodName = methodName.substring(methodName.lastIndexOf("/") + 1);
		if (StringUtils.isBlank(account) || StringUtils.isBlank(password)) {
			return SRemoteResult.initError("接口帐号密码错误");
		}

		boolean bool = invokeExceededTimes(1, "getApiToken", account, 20L, (long) (24 * 3600));
		if (bool == false) {
			return SRemoteResult.initError("接口每日调用次数已达上限");
		}
		ApiUser apiUser = null;
		List<ApiUser> list = apiUserService.find("ctime desc", apiUserService.initQbuilder(
				new String[] { "account", "password", "state" }, new Object[] { account, password,
						1 }), 1);
		if (list.size() > 0) {
			apiUser = list.get(0);
		}
		if (apiUser == null) {
			return SRemoteResult.initError("接口帐号密码错误或账号被禁用");
		}
		if (StringUtils.isNotBlank(apiUser.getAllowIps())) {
			List<String> ips = UtilConvert.string2List(apiUser.getAllowIps());
			if (!ips.contains(ip)) {
				return SRemoteResult.initError("当前IP没有调用权限");
			}
		}
		apiTokenService.deleteByApiUserId(apiUser.getId());
		ApiToken token = new ApiToken();
		token.insertBefore();
		token.setApiUserId(apiUser.getId());
		token.setApiUserAccount(apiUser.getAccount());
		token.setApiToken(SUtilRandom.randomString(20));
		token.setStart_time_long(System.currentTimeMillis());
		token.setEffective(ApiToken.effective_default);
		token.setAllowIps(apiUser.getAllowIps());
		apiTokenService.insert(token);
		redisHandler.setApiToken(token.getApiToken(), token);

		ApiToken show_token = new ApiToken();
		show_token.setApiToken(token.getApiToken());
		show_token.setStart_time_long(token.getStart_time_long());
		show_token.setEffective(token.getEffective());
		return SRemoteResult.initSuccess(show_token);
	}

	@RequestMapping("/{methodName}")
	@AspectApiLogTag
	public SRemoteResult<List<JSONObject>> api(@PathVariable String methodName,
			HttpServletRequest request, String apiToken) {
		if (StringUtils.isBlank(apiToken)) {
			return SRemoteResult.initCustom(30001, "apiToken不能为空");
			// return SRemoteResult.initError("apiToken不能为空");
		}
		ApiToken apiTokenObj = redisHandler.getApiToken(apiToken);
		if (apiTokenObj == null) {
			return SRemoteResult.initCustom(30002, "该apiToken不存在或已超时，请重新获取");
			// return SRemoteResult.initError("该apiToken不存在或已超时，请重新获取");
		}
		ApiMethod api = apiMethodService.findByMethodName(methodName);
		if (api == null) {
			return SRemoteResult.initError("接口不存在");
		}
		if (api.getState() != 1) {
			return SRemoteResult.initError("该接口已关闭，暂时无法使用");
		}
		ApiUser apiUser = apiUserService.findById(apiTokenObj.getApiUserId());
		if (apiUser == null) {
			return SRemoteResult.initError("该接口账号已不存在");
		}
		String ip = WebContext.getRealIpAddress(request);
		if (StringUtils.isNotBlank(apiUser.getAllowIps())) {
			List<String> ips = UtilConvert.string2List(apiUser.getAllowIps());
			if (!ips.contains(ip)) {
				return SRemoteResult.initError("当前IP没有调用权限");
			}
		}
		ApiPerm apiPerm = apiPermService.findApiPerm(apiTokenObj.getApiUserId(), api.getId());
		if (apiPerm == null) {
			return SRemoteResult.initError("没有权限调用该接口");
		}
		String apiPermAddQuerySql = "";
		if (api.getAddQuerySqlRequired() != null && api.getAddQuerySqlRequired() == 1) {
			if (apiPerm.getQueryAll() == null || apiPerm.getQueryAll() != 1) {
				if (StringUtils.isBlank(apiPerm.getAddQuerySql())) {
					return SRemoteResult.initError("该接口权限未设置完整");
				} else {
					apiPermAddQuerySql = apiPerm.getAddQuerySql();
				}
			}
		}
		// List<ApiPerm> perms = apiPermService.find(null,
		// apiPermService.initQbuilder(new String[]{"apiUserId","apiId"}, new
		// Object[]{apiTokenObj.getApiUserId(),api.getId()}), 1);
		// if (perms.size() == 0) {
		// return SRemoteResult.initError("没有权限调用该接口");
		// }

		// ApiPerm perm = perms.get(0);
		if (api.getDayCount() != null && api.getDayCount() > 0) {

			boolean bool = invokeExceededTimes(1, methodName, apiTokenObj.getApiUserAccount(),
					api.getDayCount(), (long) (24 * 3600));
			if (bool == false) {
				return SRemoteResult.initError("接口每日调用次数已达上限");
			}
		}
		if (api.getIntervalTime() != null && api.getIntervalTime() > 0) {
			boolean bool = invokeExceededTimes(2, methodName, apiTokenObj.getApiUserAccount(), 1L,
					api.getIntervalTime());
			if (bool == false) {
				return SRemoteResult.initError("接口调用太频繁");
			}
		}
		DbInfo db = dbInfoService.findDbById(api.getDbId());
		List<ApiField> apiFields = apiFieldService.findByApiId(api.getId());
		List<DbField> dbFields = dbFieldService.findByDbId(api.getDbId());

		Map<Long, DbField> dbFieldMap = new HashMap<Long, DbField>();
		for (DbField dbField : dbFields) {
			dbFieldMap.put(dbField.getId(), dbField);
		}

		// 拼接 查询字段 如果没有设置则查询所有 即为 select *
		StringBuffer sb_select = new StringBuffer();
		for (int i = 0; i < apiFields.size(); i++) {
			if (i > 0) {
				sb_select.append(",");
			}
			sb_select.append(apiFields.get(i).getFieldName());
		}
		if (StringUtils.isBlank(sb_select)) {
			sb_select.append(" * ");
		}

		StringBuffer sb = new StringBuffer("select " + sb_select + " from " + db.getTable_name());

		// 拼接 查询条件 ，需要apiParams 里面进行设置，如果有必填项，将会自动拦截
		StringBuffer sb_where = new StringBuffer();
		if (StringUtils.isNotBlank(api.getWhereSql())) {
			sb_where.append(" " + api.getWhereSql() + " ");
		}
		if (StringUtils.isNotBlank(apiPermAddQuerySql)) {
			if (StringUtils.isNotBlank(sb_where)) {
				sb_where.append(" and ");
			}
			sb_where.append(" " + apiPermAddQuerySql + " ");
		}
		List<ApiParams> params = apiParamsService.findByApiId(api.getId());
		Map<String, String> paramValueMap = new HashMap<String, String>();
		try {
			paramValueMap = getParamsByRequest(request);
		} catch (Exception e) {
			e.printStackTrace();
			return SRemoteResult.initError("获取接口参数失败");
		}
		for (int i = 0; i < params.size(); i++) {
			ApiParams param = params.get(i);
			if (!dbFieldMap.containsKey(param.getDbFieldId())) {
				continue;
			}
			DbField dbField = dbFieldMap.get(param.getDbFieldId());
			String value = paramValueMap.get(param.getParamName());
			if (StringUtils.isBlank(value)) {
				if (param.getRequiredState() == 1) {
					return SRemoteResult.initError("参数【" + param.getParamName() + "】不能为空");
				}
			} else {
				try {
					// 拼接自定义查询条件 主要为参数类型 字符串和日期类型 需要转化 并且拼接''符号 ，条件判断类型 目前仅支持 >
					// < =
					String valueSql = "";
					if (ApiParams.PARAMTYPE_NUMBER == param.getParamType()) {
						Double num = Double.parseDouble(value);
						valueSql = " " + num;
					} else if (ApiParams.PARAMTYPE_DATE == param.getParamType()) {
						Date date = SUtilDate.format(value);
						String dateTime = SUtilDate.format(date);
						valueSql = " '" + dateTime + "' ";
					} else {
						valueSql = " '" + value + "' ";
					}
					if (StringUtils.isNotBlank(sb_where)) {
						sb_where.append(" and ");
					}

					sb_where.append(" " + dbField.getEnName() + " ");
					if (ApiParams.COMPARETYPE_LESSTHAN == param.getCompareType()) {
						sb_where.append(" < ");
					} else if (ApiParams.COMPARETYPE_GREATERTHAN == param.getCompareType()) {
						sb_where.append(" > ");
					} else {
						sb_where.append(" = ");
					}
					sb_where.append(valueSql);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		if (StringUtils.isNotBlank(sb_where)) {
			sb.append(" where " + sb_where);
		}
		Long selectSize = 100L;
		if (api.getSelectSize() != null && api.getSelectSize() > 0) {
			selectSize = api.getSelectSize();
		}
		sb.append(" limit " + selectSize);
		String sql = sb.toString();
		DbSource dbSource = dbSourceService.findById(db.getDbSourceId());
		String db_password = SUtilAes.Decrypt(dbSource.getDb_password());
		List<JSONObject> queryList = SUtilDb.queryList(dbSource.getDb_type(), dbSource.getDb_ip(),
				dbSource.getDb_port(), dbSource.getDb_name(), dbSource.getDb_account(),
				db_password, sql);
		return SRemoteResult.initSuccess(queryList);
	}

	@RequestMapping("/{methodName1}/{methodName2}")
	@AspectApiLogTag
	public SRemoteResult<List<JSONObject>> api2(@PathVariable String methodName1,
			@PathVariable String methodName2, HttpServletRequest request, String apiToken) {
		return api(methodName1 + "/" + methodName2, request, apiToken);
	}

	@RequestMapping("/{methodName1}/{methodName2}/{methodName3}")
	@AspectApiLogTag
	public SRemoteResult<List<JSONObject>> api3(@PathVariable String methodName1,
			@PathVariable String methodName2, @PathVariable String methodName3,
			HttpServletRequest request, String apiToken) {
		return api(methodName1 + "/" + methodName2 + "/" + methodName3, request, apiToken);
	}

	private static Map<String, String> getParamsByRequest(HttpServletRequest request)
			throws Exception {
		Map<String, String[]> requestParamsMap = new HashMap<String, String[]>(
				request.getParameterMap());
		Map<String, String> params_map = new HashMap<String, String>();
		for (Iterator<Map.Entry<String, String[]>> it = requestParamsMap.entrySet().iterator(); it
				.hasNext();) {
			Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) it.next();
			params_map.put(entry.getKey(), entry.getValue()[0]);
		}
		return params_map;
	}

}
